test

5.8 wait/notify方法与胖锁

在[4.3.2.2.4节]中曾经介绍过,在JRockit中,wait/notify方法总会将瘦锁膨胀为胖锁。如果应用程序频繁对某个锁执行加锁/释放的操作,而且持有锁的时间又很短,则最好将之实现为瘦锁。因此,在新创建的对象上调用wait方法或notify方法会生成一个监视器以及胖锁,从而带来性能损耗。

5.8.1 堆的大小设置不当

在设置JVM参数时,如果堆的大小设置不当,也会引起性能问题。如果堆设置得太小,就会频繁引发垃圾回收,把时间都花费在执行垃圾回收上了;如果堆设置得太大,则执行垃圾回收的平均时间就会延长,可能会导致JVM抛出OutOfMemoryError错误。因此,分析应用程序的行为,找出最合适的堆大小是很有意义。JRockit Mission Control套件可以通过分析应用程序的行为得出与内存使用相关的数据。

5.8.2 存活对象过多

正如在3.3节介绍的,在自动内存管理系统中,运行时的复杂度主要取决于堆中存活对象的总量,而不是堆本身的大小,大量的存活对象几乎总是会增大垃圾回收的执行开销。内存分析可以帮助找出那些被本应该已经被回收掉的大对象,JRockit Mission Control套件中的内存泄漏分析工具就可以很好的完成这个任务。

5.8.3 Java并非银弹

Java是一种强大的通用编程语言,因其友好的语义和内建的内存管理而大大加快的应用程序的开发进度,但Java不是万能的,这里来谈谈不宜使用Java解决的场景:

  • 要开发一个有近实时性要求的电信应用程序,并且其中会有成千上万个线程并发执行
  • 应用程序的数据库层所返回的数据经常是20MB的字节数组
  • 应用程序性能和行为的确定性,完全依赖于底层操作系统的调度器,即使调度语义有微小变化也会对应用程序性能产生较大影响
  • 开发设备驱动程序
  • 使用C/Fortran/COBOL等语言开发的历史遗留代码太多,目前团队手中还没有好用的工具可以将这些代码转换为Java代码
  • 应用程序的并发程度很高,即按照分治策略,在最终融合各个子问题的结果之前,使用成千上万个线程共同进行计算

除了上面的示例外,还有其他很多场景不适宜使用Java。通过JVM对底层操作系统的抽象,Java实现的"一次编写,到处运行"的目的,也因此受到了广泛关注。但夸大一点说,ANSI C也能做到"一次编写,到处运行",只不过在编写源代码时,要花很多精力来应对可移植性问题,因此要结合实际场景选择合适的工具。Java是好用,但也不要滥用。